package com.juyu.miliao.api.service;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.Set;

/**
 * 删除redis 大key
 * 注:string类型控制在10KB以内，hash、list、set、zset元素个数不要超过5000,删除大Key会阻塞,
 * 特别注意超时自动删除
 *
 * @author jh
 * 2018/10/11
 */
@Service
@Slf4j
public class RemoveBigRedisKeyService {

    /**
     * 默认count为10
     */
    public static final int DEFAULT_BIGKEY_COUNT = 10;

    /**
     * 默认超过10个使用pipelined
     */
    private static final int DEFAULT_EXECUTE_PIPELINED_SIZE = 10;


    // TODO: 2018/10/15 异步

    /**
     * 删除大key set,这个肯定会造成时间更久,建议异步
     *
     * @param stringRedisTemplate stringRedisTemplate
     * @param key                 key
     * @param count               1次查多少个
     * @param pattern             匹配
     */
    public static void delBigSet(StringRedisTemplate stringRedisTemplate, String key, int count, String pattern) {
        ScanOptions scanOptions;
        if (StringUtils.isNotEmpty(pattern)) {
            //这个count不起作用,全返回,猜测是内部自动请求游标
            scanOptions = ScanOptions.scanOptions().count(count).match(pattern).build();
        } else {
            scanOptions = ScanOptions.scanOptions().count(count).build();
        }

        try (Cursor<String> cursor = stringRedisTemplate.opsForSet().scan(key, scanOptions)) {
            //这个一次性返回,用pipeLine加速
            Set<String> set = new HashSet<>();
            while (cursor.hasNext()) {
                set.add(cursor.next());
                if (set.size() > DEFAULT_EXECUTE_PIPELINED_SIZE) {
                    final String[] strings = new String[set.size()];
                    //SoftReference
                    set.toArray(strings);
                    stringRedisTemplate.executePipelined(
                            (RedisCallback<Boolean>) connection -> {
                                StringRedisConnection stringRedisConn = (StringRedisConnection) connection;
                                stringRedisConn.sRem(key, strings);
                                return null;
                            });
                    set.clear();
                }
            }
            //把最后的删除
            if (!set.isEmpty()) {
                Object[] strings = new String[set.size()];
                set.toArray(strings);
                stringRedisTemplate.opsForSet().remove(key, strings);
            }
        } catch (Exception e) {
            log.error("删除大key:set:msg:{}", e.getMessage());
        }
    }

}
